namespace Nintest.Model
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;

    using Nintest.Builder;

    public class TestRunner
    {
        private readonly Assembly _assembly;
        private readonly IList<Type> _fixureTypes;

        public TestRunner(Assembly assembly)
        {
            _assembly = assembly;
            _fixureTypes = new List<Type>();
            AddFixtureTypes(assembly);
        }

        public event EventHandler<TraceEventArgs> Trace;

        public void Run()
        {
            try
            {
                FireTrace(TraceLevel.Debug, "Started");
                foreach (var fixureType in _fixureTypes)
                {
                    FireTrace(TraceLevel.Info, "Found fixture: " + fixureType);
                    RunFixture(fixureType);
                }

                FireTrace(TraceLevel.Debug, "Finished without errors");
            }
            catch (Exception ex)
            {
                FireTrace(TraceLevel.Error, "Finished with error: " + ex);
            }
        }

        private void RunFixture(Type fixureType)
        {
            var testsBase = typeof(Tests<>).MakeGenericType(fixureType);
            var tests = new List<Type>();
            _assembly.GetTypes()
                .Where(type => type.IsSubclassOf(testsBase))
                .ForEach(tests.Add);
            foreach (var testClass in tests)
            {
                FireTrace(TraceLevel.Info, "Found test-class: " + testClass);
                BuildTestClass(testClass);
            }
        }

        private void BuildTestClass(Type testClass)
        {
            throw new NotImplementedException();
        }

        private void AddFixtureTypes(Assembly assembly)
        {
            assembly.GetTypes()
                .Where(type => type.IsSubclassOf(typeof(TestFixture)))
                .ForEach(_fixureTypes.Add);
        }

        private void FireTrace(TraceLevel level, string message)
        {
            Trace.SafeInvoke(this, TraceEventArgs.Create(level, message));
        }
    }
}